/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.attilax.reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.ClassUtils;

import com.attilax.exception.ExUtil;
//r33 import com.attilax.office.excelUtil;
import com.attilax.ref.refx;
import com.google.common.collect.Lists;

/**
 * <p>
 * Utility reflection methods focused on methods, originally from Commons
 * BeanUtils. Differences from the BeanUtils version may be noted, especially
 * where similar functionality already existed within Lang.
 * </p>
 *
 * <h3>Known Limitations</h3> <h4>Accessing Public Methods In A Default Access
 * Superclass</h4>
 * <p>
 * There is an issue when invoking public methods contained in a default access
 * superclass on JREs prior to 1.4. Reflection locates these methods fine and
 * correctly assigns them as public. However, an
 * <code>IllegalAccessException</code> is thrown if the method is invoked.
 * </p>
 *
 * <p>
 * <code>MethodUtils</code> contains a workaround for this situation. It will
 * attempt to call <code>setAccessible</code> on this method. If this call
 * succeeds, then the method can be invoked as normal. This call will only
 * succeed when the application has sufficient security privileges. If this call
 * fails then the method may fail.
 * </p>
 *
 * @since 2.5
 * @version $Id: MethodUtils.java 1166253 2011-09-07 16:27:42Z ggregory $
 */
public class MethodUtils {

	/**
	 * <p>
	 * MethodUtils instances should NOT be constructed in standard programming.
	 * Instead, the class should be used as
	 * <code>MethodUtils.getAccessibleMethod(method)</code>.
	 * </p>
	 *
	 * <p>
	 * This constructor is public to permit tools that require a JavaBean
	 * instance to operate.
	 * </p>
	 */
	public MethodUtils() {
		super();
	}

	/**
	 * <p>
	 * Invokes a named method whose parameter type matches the object type.
	 * </p>
	 *
	 * <p>
	 * This method delegates the method search to
	 * {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
	 * </p>
	 *
	 * <p>
	 * This method supports calls to methods taking primitive parameters via
	 * passing in wrapping classes. So, for example, a <code>Boolean</code>
	 * object would match a <code>boolean</code> primitive.
	 * </p>
	 *
	 * <p>
	 * This is a convenient wrapper for
	 * {@link #invokeMethod(Object object,String methodName, Object[] args, Class[] parameterTypes)}
	 * .
	 * </p>
	 *
	 * @param object
	 *            invoke method on this object
	 * @param methodName
	 *            get method with this name
	 * @param args
	 *            use these arguments - treat null as empty array
	 * @return The value returned by the invoked method
	 *
	 * @throws NoSuchMethodException
	 *             if there is no such accessible method
	 * @throws InvocationTargetException
	 *             wraps an exception thrown by the method invoked
	 * @throws IllegalAccessException
	 *             if the requested method is not accessible via reflection
	 */
	public static Object invokeMethod(Object object, String methodName,
			Object... args) throws NoSuchMethodException,
			IllegalAccessException, InvocationTargetException {
		if (args == null) {
			args = ArrayUtils.EMPTY_OBJECT_ARRAY;
		}
		int arguments = args.length;
		Class<?>[] parameterTypes = new Class[arguments];
		for (int i = 0; i < arguments; i++) {
			Object param = args[i];
			if (param == null)
				parameterTypes[i] = Object.class;
			else
				parameterTypes[i] = args[i].getClass();
		}
		try {
			return invokeMethod(object, methodName, args, parameterTypes);
		} catch (NoSuchMethodException e) {
			try {
				Method m1 = refx.getMeth(object.getClass(), methodName, args);
				Object[] args_convertTypeed=convertType(args,m1);
				Object ret = m1.invoke(object, args_convertTypeed);
				return ret;
			} catch (Throwable e2) {
				e2.printStackTrace();
				throw new RuntimeException("cant find mothod---cls:"+object+" method:"+methodName+" param:"+args , e2);
			}
		
		}
	
	}

	private static Object[] convertType(Object[] args, Method m1) {
		@SuppressWarnings("rawtypes")
		Class[] paramTypes=m1.getParameterTypes();
		if(paramTypes.length!=args.length)
			throw new RuntimeException("args leng not match the method params leng:"+args.length+"/"+paramTypes.length);
		List li=Lists.newArrayList();
		for (int i = 0; i < paramTypes.length; i++) {
			Object arg_converted=convertType(args[i], paramTypes[i]);
			 li.add(arg_converted);

		}
		return li.toArray();
	}

	private static Object convertType(Object object, Class  paramTypes) {
		 
		if(paramTypes==Integer.class ||paramTypes==int.class )			
			return Integer.parseInt(object.toString());
		return object;
	}

	/**
	 * <p>
	 * Invokes a named method whose parameter type matches the object type.
	 * </p>
	 *
	 * <p>
	 * This method delegates the method search to
	 * {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
	 * </p>
	 *
	 * <p>
	 * This method supports calls to methods taking primitive parameters via
	 * passing in wrapping classes. So, for example, a <code>Boolean</code>
	 * object would match a <code>boolean</code> primitive.
	 * </p>
	 *
	 * @param object
	 *            invoke method on this object
	 * @param methodName
	 *            get method with this name
	 * @param args
	 *            use these arguments - treat null as empty array
	 * @param parameterTypes
	 *            match these parameters - treat null as empty array
	 * @return The value returned by the invoked method
	 *
	 * @throws NoSuchMethodException
	 *             if there is no such accessible method
	 * @throws InvocationTargetException
	 *             wraps an exception thrown by the method invoked
	 * @throws IllegalAccessException
	 *             if the requested method is not accessible via reflection
	 */
	public static Object invokeMethod(Object object, String methodName,
			Object[] args, Class<?>[] parameterTypes)
			throws NoSuchMethodException, IllegalAccessException,
			InvocationTargetException {
		if (parameterTypes == null) {
			parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
		}
		if (args == null) {
			args = ArrayUtils.EMPTY_OBJECT_ARRAY;
		}
		Method method = getMatchingAccessibleMethod(object.getClass(),
				methodName, parameterTypes);
		if (method == null) {
			throw new NoSuchMethodException("No such accessible method: "
					+ methodName + "() on object: "
					+ object.getClass().getName());
		}
		return method.invoke(object, args);
	}

	/**
	 * <p>
	 * Invokes a method whose parameter types match exactly the object types.
	 * </p>
	 *
	 * <p>
	 * This uses reflection to invoke the method obtained from a call to
	 * <code>getAccessibleMethod()</code>.
	 * </p>
	 *
	 * @param object
	 *            invoke method on this object
	 * @param methodName
	 *            get method with this name
	 * @param args
	 *            use these arguments - treat null as empty array
	 * @return The value returned by the invoked method
	 *
	 * @throws NoSuchMethodException
	 *             if there is no such accessible method
	 * @throws InvocationTargetException
	 *             wraps an exception thrown by the method invoked
	 * @throws IllegalAccessException
	 *             if the requested method is not accessible via reflection
	 */
	public static Object invokeExactMethod(Object object, String methodName,
			Object... args) throws NoSuchMethodException,
			IllegalAccessException, InvocationTargetException {
		if (args == null) {
			args = ArrayUtils.EMPTY_OBJECT_ARRAY;
		}
		int arguments = args.length;
		Class<?>[] parameterTypes = new Class[arguments];
		for (int i = 0; i < arguments; i++) {
			parameterTypes[i] = args[i].getClass();
		}
		return invokeExactMethod(object, methodName, args, parameterTypes);
	}

	/**
	 * <p>
	 * Invokes a method whose parameter types match exactly the parameter types
	 * given.
	 * </p>
	 *
	 * <p>
	 * This uses reflection to invoke the method obtained from a call to
	 * <code>getAccessibleMethod()</code>.
	 * </p>
	 *
	 * @param object
	 *            invoke method on this object
	 * @param methodName
	 *            get method with this name
	 * @param args
	 *            use these arguments - treat null as empty array
	 * @param parameterTypes
	 *            match these parameters - treat null as empty array
	 * @return The value returned by the invoked method
	 *
	 * @throws NoSuchMethodException
	 *             if there is no such accessible method
	 * @throws InvocationTargetException
	 *             wraps an exception thrown by the method invoked
	 * @throws IllegalAccessException
	 *             if the requested method is not accessible via reflection
	 */
	public static Object invokeExactMethod(Object object, String methodName,
			Object[] args, Class<?>[] parameterTypes)
			throws NoSuchMethodException, IllegalAccessException,
			InvocationTargetException {
		if (args == null) {
			args = ArrayUtils.EMPTY_OBJECT_ARRAY;
		}
		if (parameterTypes == null) {
			parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
		}
		Method method = getAccessibleMethod(object.getClass(), methodName,
				parameterTypes);
		if (method == null) {
			throw new NoSuchMethodException("No such accessible method: "
					+ methodName + "() on object: "
					+ object.getClass().getName());
		}
		return method.invoke(object, args);
	}

	/**
	 * <p>
	 * Invokes a static method whose parameter types match exactly the parameter
	 * types given.
	 * </p>
	 *
	 * <p>
	 * This uses reflection to invoke the method obtained from a call to
	 * {@link #getAccessibleMethod(Class, String, Class[])}.
	 * </p>
	 *
	 * @param cls
	 *            invoke static method on this class
	 * @param methodName
	 *            get method with this name
	 * @param args
	 *            use these arguments - treat null as empty array
	 * @param parameterTypes
	 *            match these parameters - treat null as empty array
	 * @return The value returned by the invoked method
	 *
	 * @throws NoSuchMethodException
	 *             if there is no such accessible method
	 * @throws InvocationTargetException
	 *             wraps an exception thrown by the method invoked
	 * @throws IllegalAccessException
	 *             if the requested method is not accessible via reflection
	 */
	public static Object invokeExactStaticMethod(Class<?> cls,
			String methodName, Object[] args, Class<?>[] parameterTypes)
			throws NoSuchMethodException, IllegalAccessException,
			InvocationTargetException {
		if (args == null) {
			args = ArrayUtils.EMPTY_OBJECT_ARRAY;
		}
		if (parameterTypes == null) {
			parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
		}
		Method method = getAccessibleMethod(cls, methodName, parameterTypes);
		if (method == null) {
			throw new NoSuchMethodException("No such accessible method: "
					+ methodName + "() on class: " + cls.getName());
		}
		return method.invoke(null, args);
	}

	/**
	 * <p>
	 * Invokes a named static method whose parameter type matches the object
	 * type.
	 * </p>
	 *
	 * <p>
	 * This method delegates the method search to
	 * {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
	 * </p>
	 *
	 * <p>
	 * This method supports calls to methods taking primitive parameters via
	 * passing in wrapping classes. So, for example, a <code>Boolean</code>
	 * class would match a <code>boolean</code> primitive.
	 * </p>
	 *
	 * <p>
	 * This is a convenient wrapper for
	 * {@link #invokeStaticMethod(Class objectClass,String methodName,Object [] args,Class[] parameterTypes)}
	 * .
	 * </p>
	 *
	 * @param cls
	 *            invoke static method on this class
	 * @param methodName
	 *            get method with this name
	 * @param args
	 *            use these arguments - treat null as empty array
	 * @return The value returned by the invoked method
	 *
	 * @throws NoSuchMethodException
	 *             if there is no such accessible method
	 * @throws InvocationTargetException
	 *             wraps an exception thrown by the method invoked
	 * @throws IllegalAccessException
	 *             if the requested method is not accessible via reflection
	 */
	public static Object invokeStaticMethod(Class<?> cls, String methodName,
			Object... args) throws NoSuchMethodException,
			IllegalAccessException, InvocationTargetException {
		if (args == null) {
			args = ArrayUtils.EMPTY_OBJECT_ARRAY;
		}
		int arguments = args.length;
		Class<?>[] parameterTypes = new Class[arguments];
		for (int i = 0; i < arguments; i++) {
			parameterTypes[i] = args[i].getClass();
		}
		return invokeStaticMethod(cls, methodName, args, parameterTypes);
	}

	/**
	 * <p>
	 * Invokes a named static method whose parameter type matches the object
	 * type.
	 * </p>
	 *
	 * <p>
	 * This method delegates the method search to
	 * {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
	 * </p>
	 *
	 * <p>
	 * This method supports calls to methods taking primitive parameters via
	 * passing in wrapping classes. So, for example, a <code>Boolean</code>
	 * class would match a <code>boolean</code> primitive.
	 * </p>
	 *
	 *
	 * @param cls
	 *            invoke static method on this class
	 * @param methodName
	 *            get method with this name
	 * @param args
	 *            use these arguments - treat null as empty array
	 * @param parameterTypes
	 *            match these parameters - treat null as empty array
	 * @return The value returned by the invoked method
	 *
	 * @throws NoSuchMethodException
	 *             if there is no such accessible method
	 * @throws InvocationTargetException
	 *             wraps an exception thrown by the method invoked
	 * @throws IllegalAccessException
	 *             if the requested method is not accessible via reflection
	 */
	public static Object invokeStaticMethod(Class<?> cls, String methodName,
			Object[] args, Class<?>[] parameterTypes)
			throws NoSuchMethodException, IllegalAccessException,
			InvocationTargetException {
		if (parameterTypes == null) {
			parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
		}
		if (args == null) {
			args = ArrayUtils.EMPTY_OBJECT_ARRAY;
		}
		Method method = getMatchingAccessibleMethod(cls, methodName,
				parameterTypes);
		if (method == null) {
			throw new NoSuchMethodException("No such accessible method: "
					+ methodName + "() on class: " + cls.getName());
		}
		return method.invoke(null, args);
	}

	/**
	 * <p>
	 * Invokes a static method whose parameter types match exactly the object
	 * types.
	 * </p>
	 *
	 * <p>
	 * This uses reflection to invoke the method obtained from a call to
	 * {@link #getAccessibleMethod(Class, String, Class[])}.
	 * </p>
	 *
	 * @param cls
	 *            invoke static method on this class
	 * @param methodName
	 *            get method with this name
	 * @param args
	 *            use these arguments - treat null as empty array
	 * @return The value returned by the invoked method
	 *
	 * @throws NoSuchMethodException
	 *             if there is no such accessible method
	 * @throws InvocationTargetException
	 *             wraps an exception thrown by the method invoked
	 * @throws IllegalAccessException
	 *             if the requested method is not accessible via reflection
	 */
	public static Object invokeExactStaticMethod(Class<?> cls,
			String methodName, Object... args) throws NoSuchMethodException,
			IllegalAccessException, InvocationTargetException {
		if (args == null) {
			args = ArrayUtils.EMPTY_OBJECT_ARRAY;
		}
		int arguments = args.length;
		Class<?>[] parameterTypes = new Class[arguments];
		for (int i = 0; i < arguments; i++) {
			parameterTypes[i] = args[i].getClass();
		}
		return invokeExactStaticMethod(cls, methodName, args, parameterTypes);
	}

	/**
	 * <p>
	 * Returns an accessible method (that is, one that can be invoked via
	 * reflection) with given name and parameters. If no such method can be
	 * found, return <code>null</code>. This is just a convenient wrapper for
	 * {@link #getAccessibleMethod(Method method)}.
	 * </p>
	 *
	 * @param cls
	 *            get method from this class
	 * @param methodName
	 *            get method with this name
	 * @param parameterTypes
	 *            with these parameters types
	 * @return The accessible method
	 */
	public static Method getAccessibleMethod(Class<?> cls, String methodName,
			Class<?>... parameterTypes) {
		try {
			return getAccessibleMethod(cls
					.getMethod(methodName, parameterTypes));
		} catch (NoSuchMethodException e) {
			return null;
		}
	}

	/**
	 * <p>
	 * Returns an accessible method (that is, one that can be invoked via
	 * reflection) that implements the specified Method. If no such method can
	 * be found, return <code>null</code>.
	 * </p>
	 *
	 * @param method
	 *            The method that we wish to call
	 * @return The accessible method
	 */
	public static Method getAccessibleMethod(Method method) {
		if (!MemberUtils.isAccessible(method)) {
			return null;
		}
		// If the declaring class is public, we are done
		Class<?> cls = method.getDeclaringClass();
		if (Modifier.isPublic(cls.getModifiers())) {
			return method;
		}
		String methodName = method.getName();
		Class<?>[] parameterTypes = method.getParameterTypes();

		// Check the implemented interfaces and subinterfaces
		method = getAccessibleMethodFromInterfaceNest(cls, methodName,
				parameterTypes);

		// Check the superclass chain
		if (method == null) {
			method = getAccessibleMethodFromSuperclass(cls, methodName,
					parameterTypes);
		}
		return method;
	}

	/**
	 * <p>
	 * Returns an accessible method (that is, one that can be invoked via
	 * reflection) by scanning through the superclasses. If no such method can
	 * be found, return <code>null</code>.
	 * </p>
	 *
	 * @param cls
	 *            Class to be checked
	 * @param methodName
	 *            Method name of the method we wish to call
	 * @param parameterTypes
	 *            The parameter type signatures
	 * @return the accessible method or <code>null</code> if not found
	 */
	private static Method getAccessibleMethodFromSuperclass(Class<?> cls,
			String methodName, Class<?>... parameterTypes) {
		Class<?> parentClass = cls.getSuperclass();
		while (parentClass != null) {
			if (Modifier.isPublic(parentClass.getModifiers())) {
				try {
					return parentClass.getMethod(methodName, parameterTypes);
				} catch (NoSuchMethodException e) {
					return null;
				}
			}
			parentClass = parentClass.getSuperclass();
		}
		return null;
	}

	/**
	 * <p>
	 * Returns an accessible method (that is, one that can be invoked via
	 * reflection) that implements the specified method, by scanning through all
	 * implemented interfaces and subinterfaces. If no such method can be found,
	 * return <code>null</code>.
	 * </p>
	 *
	 * <p>
	 * There isn't any good reason why this method must be private. It is
	 * because there doesn't seem any reason why other classes should call this
	 * rather than the higher level methods.
	 * </p>
	 *
	 * @param cls
	 *            Parent class for the interfaces to be checked
	 * @param methodName
	 *            Method name of the method we wish to call
	 * @param parameterTypes
	 *            The parameter type signatures
	 * @return the accessible method or <code>null</code> if not found
	 */
	private static Method getAccessibleMethodFromInterfaceNest(Class<?> cls,
			String methodName, Class<?>... parameterTypes) {
		Method method = null;

		// Search up the superclass chain
		for (; cls != null; cls = cls.getSuperclass()) {

			// Check the implemented interfaces of the parent class
			Class<?>[] interfaces = cls.getInterfaces();
			for (int i = 0; i < interfaces.length; i++) {
				// Is this interface public?
				if (!Modifier.isPublic(interfaces[i].getModifiers())) {
					continue;
				}
				// Does the method exist on this interface?
				try {
					method = interfaces[i].getDeclaredMethod(methodName,
							parameterTypes);
				} catch (NoSuchMethodException e) { // NOPMD
					/*
					 * Swallow, if no method is found after the loop then this
					 * method returns null.
					 */
				}
				if (method != null) {
					break;
				}
				// Recursively check our parent interfaces
				method = getAccessibleMethodFromInterfaceNest(interfaces[i],
						methodName, parameterTypes);
				if (method != null) {
					break;
				}
			}
		}
		return method;
	}

	/**
	 * <p>
	 * Finds an accessible method that matches the given name and has compatible
	 * parameters. Compatible parameters mean that every method parameter is
	 * assignable from the given parameters. In other words, it finds a method
	 * with the given name that will take the parameters given.
	 * <p>
	 *
	 * <p>
	 * This method is used by
	 * {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
	 *
	 * <p>
	 * This method can match primitive parameter by passing in wrapper classes.
	 * For example, a <code>Boolean</code> will match a primitive
	 * <code>boolean</code> parameter.
	 *
	 * @param cls
	 *            find method in this class
	 * @param methodName
	 *            find method with this name
	 * @param parameterTypes
	 *            find method with most compatible parameters
	 * @return The accessible method
	 */
	public static Method getMatchingAccessibleMethod(Class<?> cls,
			String methodName, Class<?>... parameterTypes) {
		try {
			Method method = cls.getMethod(methodName, parameterTypes);
			MemberUtils.setAccessibleWorkaround(method);
			return method;
		} catch (NoSuchMethodException e) { // NOPMD - Swallow the exception
		}
		// search through all methods
		Method bestMatch = null;
		Method[] methods = cls.getMethods();
		for (Method method : methods) {
			// compare name and parameters
			if (method.getName().equals(methodName)
					&& ClassUtils.isAssignable(parameterTypes,
							method.getParameterTypes(), true)) {
				// get accessible version of method
				Method accessibleMethod = getAccessibleMethod(method);
				if (accessibleMethod != null
						&& (bestMatch == null || MemberUtils
								.compareParameterTypes(
										accessibleMethod.getParameterTypes(),
										bestMatch.getParameterTypes(),
										parameterTypes) < 0)) {
					bestMatch = accessibleMethod;
				}
			}
		}
		if (bestMatch != null) {
			MemberUtils.setAccessibleWorkaround(bestMatch);
		}
		return bestMatch;
	}

	public static Object invokeStaticMethod(String clsName, String methodName,
			Object class1) {
		System.out.println("");
		Class<?> cls;
		try {
			cls = Class.forName(clsName);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new RuntimeException(e);
		}
		try {
			//class com.attilax.ioc.Ioc4pay  getbean  class com.attilax.wechat.WeChatPayUtil
			return org.apache.commons.lang3.reflect.MethodUtils
					.invokeStaticMethod(cls, methodName, class1);
		} catch (Exception e) {

			ExUtil.throwExV2(e);
		}
		return "$ex";

	}
}
